package com.shuiyun.jtt.endpoint;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.asymmetric.RSA;
import com.shuiyun.jtt.anno.Endpoint;
import com.shuiyun.jtt.anno.Mapping;
import com.shuiyun.jtt.entity.bo.DeviceInfo;
import com.shuiyun.jtt.entity.vo.DeviceInfoVO;
import com.shuiyun.jtt.enums.JT808;
import com.shuiyun.jtt.enums.MultimediaType;
import com.shuiyun.jtt.message.Header;
import com.shuiyun.jtt.message.JTMessage;
import com.shuiyun.jtt.message.jt808.*;
import com.shuiyun.jtt.service.DeviceCacheService;
import com.shuiyun.jtt.session.Session;
import com.shuiyun.jtt.session.SessionManager;
import com.shuiyun.jtt.utils.CacheKey;
import com.shuiyun.jtt.utils.TachographUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.Date;

/**
 * @author shuiyun
 * @description 808协议处理器
 * @date 2022-04-20 00:04
 */
@Slf4j
@Endpoint
public class JT808Endpoint {

    /** 固定文件缓存目录 */
    private final static String FILE_PATH = "data/";

    @Autowired
    private SessionManager sessionManager;
    @Autowired
    private DeviceCacheService deviceCacheService;

    @Mapping(types = JT808.终端通用应答0x0001,desc = "终端通用应答")
    public void t0001(T0001 message,Session session){
        log.info("收到设备的通用应答:{}",session);
    }

    @Mapping(types = JT808.终端注册0x0100,desc = "终端注册")
    public T8100 t0100(T0100 message, Session session){
        Header header = message.getHeader();
        // TODO 这里按照实际业务需求进行注册校验 我这里因为没有连接数据库也没有查询接口 所有仅用缓存来维护数据
        String mobileNo = header.getMobileNo();
        T0100 t0100 = CacheKey.instance.getRegisterDeviceInfoMap().get(mobileNo);
        T8100 result = new T8100(session.nextSerialNo(), header.getMobileNo());
        if (ObjectUtil.isNotNull(t0100)){
            result.setResultCode(T8100.AlreadyRegisteredTerminal);
            log.info("{}重复注册",session);
            return result;
        }
        CacheKey.instance.getRegisterDeviceInfoMap().put(mobileNo,message);
        DeviceInfo device = new DeviceInfo(message);
        session.register(message.getHeader(), device);
        // 生成鉴权码
        result.setToken(DeviceInfo.getToken(device));
        return result;
    }

    @Mapping(types = JT808.终端鉴权0x0102,desc = "终端鉴权")
    public T0001 t0102(T0102 message, Session session,Header header){
        T0001 result = new T0001(session.nextSerialNo(), header.getMobileNo());
        result.setSerialNo(header.getSerialNo());
        result.setReplyId(header.getMessageId());

        // 这里可以根据设备信息进行鉴权操作
        boolean auth = deviceCacheService.auth(message);
        Session registerSession = sessionManager.get(header.getClientId());
        if (ObjectUtil.isNotNull(registerSession) && registerSession.isRegistered() && auth){
            result.setResultCode(T0001.SUCCESS);
            return result;
        }

        // TODO 如果当这里有接入数据库 则可以根据卡号查询车辆信息完成认证鉴权操作
        result.setResultCode(T0001.FAILURE);
        return result;
    }

    @Mapping(types = JT808.终端心跳0x0002,desc = "终端心跳")
    public T0001 t0002(JTMessage message,Session session,Header header){
        log.debug("收到设备心跳：{}",session);
        // 获取会自动刷新过期时间
        DeviceInfoVO heart = deviceCacheService.heart(header.getMobileNo());
        // 设备不在线 关闭通道
        if (ObjectUtil.isNull(header)){
            session.getChannel().close();
        }
        return getDefaultResponse(session, header);
    }

    /**
     * 获取通用应答
     * @param session 请求方
     * @param header 头信息
     * @author shuiyun
     * @date 2022/4/27 0:07
     * @return 通用应答消息
     */
    private T0001 getDefaultResponse(Session session, Header header) {
        T0001 result = new T0001(session.nextSerialNo(), header.getMobileNo());
        result.setSerialNo(header.getSerialNo());
        result.setReplyId(header.getMessageId());
        result.setResultCode(T0001.SUCCESS);
        return result;
    }

    @Mapping(types = JT808.查询服务器时间0x0004,desc = "查询服务器时间")
    public T8004 t0004(JTMessage message,Session session,Header header){
        T8004 result = new T8004(session.nextSerialNo(), header.getMobileNo());
        result.setServerTime(new Date());
        return result;
    }

    @Mapping(types = JT808.终端注销0x0003,desc = "终端注销")
    public void t0003(JTMessage message,Session session){
        session.invalidate();
    }

    @Mapping(types = JT808.查询终端参数应答0x0104,desc = "查询终端参数应答")
    public T0001 t0104(T0104 message,Session session,Header header){
        log.info("{} 查询终端参数应答：{}",session,message.getTerminalParam());
        return getDefaultResponse(session,header);
    }

    @Mapping(types = JT808.查询终端属性应答0x0107,desc = "查询终端属性应答")
    public T0001 t0107(T0107 message,Header header,Session session){
        log.info("{} 查询终端属性应答：{}",session,message);
        return getDefaultResponse(session,header);
    }

    @Mapping(types = JT808.终端升级结果通知0x0108,desc = "终端升级结果通知")
    public T0001 t0108(T0108 message,Header header,Session session){
        log.info("{} 终端升级结果通知：{}",session,message);
        return getDefaultResponse(session,header);
    }

    @Mapping(types = JT808.位置信息汇报0x0200,desc = "位置信息上报")
    public T0001 location(T0200 message,Session session,Header header){
        log.info("{} 发送的定位信息：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = {JT808.位置信息查询应答0x0201,JT808.车辆控制应答0x0500},desc = "位置信息查询")
    public T0001 realLocation(T0201And0500 message, Session session, Header header){
        if (JT808.位置信息查询应答0x0201 == header.getMessageId()){
            log.info("{} 发送的实时定位信息：{}",session,message);
        }else{
            log.info("{} 车辆控制应答：{}",session,message);
        }
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.查询区域或线路数据应答0x0608,desc = "查询区域或线路数据")
    public T0001 queryArea(T0608 message,Session session,Header header){
        log.info("{} 发送的区域查询应答:{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.行驶记录数据上传0x0700,desc = "行驶记录数据上传")
    public T0001 t0700(T0700 message,Session session,Header header){
        log.info("{} 行驶记录数据上传:{}",session, TachographUtil.decode(message.getContent()));
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.电子运单上报0x0701,desc = "电子运单上传")
    public T0001 waybill(T0701 message,Session session,Header header){
        log.info("{} 发送的电子运单信息：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.驾驶员身份信息采集上报0x0702,desc = "驾驶员身份信息采集上报")
    public T0001 t0702(T0702 message,Session session,Header header){
        log.info("{} 发送的驾驶员信息：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.定位数据批量上传0x0704,desc = "定位数据批量上传")
    public T0001 t0704(T0704 message,Session session,Header header){
        log.info("{} 发送的批量定位信息：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.CAN总线数据上传0x0705,desc = "CAN总线数据上传")
    public T0001 t0705(T0705 message,Session session,Header header){
        log.info("{} 发送的CAN总线信息：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.多媒体事件信息上传0x0800,desc = "多媒体事件信息上传")
    public T0001 t0800(T0800 message,Session session,Header header){
        log.info("{} 发送的多媒体事件信息上传信息：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.多媒体数据上传0x0801,desc = "多媒体数据上传")
    public T8800 t0801(T0801 message,Session session,Header header){
        T8800 t8800 = new T8800();
        t8800.setHeader(new Header(JT808.多媒体数据上传应答0x8800,header.getSerialNo(),header.getMobileNo()));
        t8800.setId(message.getId());
        String postfix = MultimediaType.getPostfix(message.getCode());
        if (StrUtil.isBlank(postfix)){
            log.error("文件编码异常，无法保存,文件编码格式：{}",message.getCode());
            return t8800;
        }
        // TODO 这里可以根据实际业务需求将文件上传到oss 或者obs文件服务器
        String fileName = IdUtil.simpleUUID() + postfix;
        File file = new File(FILE_PATH + fileName);
        try (FileOutputStream outputStream = new FileOutputStream(file);){
            ByteArrayInputStream inputStream = new ByteArrayInputStream(message.getBytes());
            IoUtil.copy(inputStream,outputStream);
        }catch (Exception e){
            log.error("拷贝文件流失败");
            return t8800;
        }
        log.info("{} 发送的多媒体事件信息上传信息：{},生成文件：{}",session,message,fileName);
        return t8800;
    }


    @Mapping(types = JT808.摄像头立即拍摄命令应答0x0805,desc = "摄像头立即拍摄命令应答0x0805")
    public T0001 t0805(T0805 message,Header header,Session session){
        log.info("{} 摄像头立即拍摄命令应答：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.存储多媒体数据检索应答0x0802,desc = "存储多媒体数据检索应答0x0802")
    public T0001 t0802(T0802 message,Header header,Session session){
        log.info("{} 存储多媒体数据检索应答：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.数据上行透传0x0900,desc = "数据上行透传")
    public T0001 t0900(T0900 message,Header header,Session session){
        log.info("{} 数据上行透传：{}",session,message);
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.数据压缩上报0x0901,desc = "数据压缩上报")
    public T0001 t0901(T0901 message,Header header,Session session){
        log.info("{} 数据压缩上报：{}",session, ZipUtil.unGzip(message.getContent(),"GBK"));
        return getDefaultResponse(session, header);
    }

    @Mapping(types = JT808.终端RSA公钥0x0A00,desc = "终端RSA公钥")
    public T0001 t0a00(T0A00 message,Header header,Session session){
        log.info("{} 终端RSA公钥：{}",session, RSA.generatePublicKey(new BigInteger(message.getN()),new BigInteger(String.valueOf(message.getE()))));
        return getDefaultResponse(session, header);
    }

}
